{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- https://course.rs/advance/errors.html\n",
    "\n",
    "TL;DR：\n",
    "\n",
    "- **组合器速览**\n",
    "  - `or / and / xor`：按布尔逻辑组合 `Option` / `Result`。\n",
    "  - `or_else / and_then`：在需要惰性计算时使用闭包版本。\n",
    "  - `filter`：对 `Option` 中的值按条件筛选。\n",
    "- **映射与默认值**\n",
    "  - `map / map_err`：分别转换成功值与错误值。\n",
    "  - `map_or / map_or_else`：在映射失败时提供默认值或默认闭包。\n",
    "- **`Option` ↔ `Result` 转换**\n",
    "  - `ok_or / ok_or_else`：把 `Option` 转成带错误信息的 `Result`。\n",
    "- **自定义错误类型**\n",
    "  - 实现 `Debug`、`Display`（可选实现 `Error`）以便打印和特征对象化。\n",
    "  - 使用结构体/枚举存放错误码、消息等额外上下文。\n",
    "- **错误转换 (`From`)**\n",
    "  - 为自定义错误实现 `From`，让 `?` 自动把外部错误转成内部类型。\n",
    "- **归一化多种错误**\n",
    "  - **`Box<dyn Error>`**：快捷统一各种实现了 `Error` 的错误。\n",
    "  - **自定义枚举错误**：最灵活，可细分错误来源。\n",
    "  - **社区工具**：`thiserror`（库开发首选）、`error-chain`（宏生成完整框架）、`anyhow`（应用/脚本快速处理）。\n",
    "- **结论**\n",
    "   熟练掌握组合器与转换工具，可让错误处理既简洁又具表现力；针对场景选择合适的归一化策略，事半功倍。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在之前的[返回值和错误处理](https://course.rs/basic/result-error/intro.html)章节中，我们学习了几个重要的概念，例如 `Result` 用于返回结果处理，`?` 用于错误的传播，若大家对此还较为模糊，强烈建议回头温习下。\n",
    "\n",
    "在本章节中一起来看看如何对 `Result` ( `Option` ) 做进一步的处理，以及如何定义自己的错误类型。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# [组合器](https://course.rs/advance/errors.html#组合器)\n",
    "\n",
    "在设计模式中，有一个组合器模式，相信有 Java 背景的同学对此并不陌生。\n",
    "\n",
    "> 将对象组合成树形结构以表示“部分整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。–GoF <<设计模式>>\n",
    "\n",
    "与组合器模式有所不同，在 Rust 中，组合器更多的是用于对返回结果的类型进行变换：例如使用 `ok_or` 将一个 `Option` 类型转换成 `Result` 类型。\n",
    "\n",
    "下面我们来看看一些常见的组合器。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [or() 和 and()](https://course.rs/advance/errors.html#or-和-and)\n",
    "\n",
    "跟布尔关系的与/或很像，这两个方法会对两个表达式做逻辑组合，最终返回 `Option` / `Result`。\n",
    "\n",
    "- `or()`，表达式按照顺序求值，若任何一个表达式的结果是 `Some` 或 `Ok`，则该值会立刻返回\n",
    "- `and()`，若两个表达式的结果都是 `Some` 或 `Ok`，则**第二个表达式中的值被返回**。若任何一个的结果是 `None` 或 `Err` ，则立刻返回。\n",
    "\n",
    "实际上，只要将布尔表达式的 `true` / `false`，替换成 `Some` / `None` 或 `Ok` / `Err` 就很好理解了。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "fn main() {\n",
    "  let s1 = Some(\"some1\");\n",
    "  let s2 = Some(\"some2\");\n",
    "  let n: Option<&str> = None;\n",
    "\n",
    "  let o1: Result<&str, &str> = Ok(\"ok1\");\n",
    "  let o2: Result<&str, &str> = Ok(\"ok2\");\n",
    "  let e1: Result<&str, &str> = Err(\"error1\");\n",
    "  let e2: Result<&str, &str> = Err(\"error2\");\n",
    "\n",
    "  assert_eq!(s1.or(s2), s1); // Some1 or Some2 = Some1\n",
    "  assert_eq!(s1.or(n), s1);  // Some or None = Some\n",
    "  assert_eq!(n.or(s1), s1);  // None or Some = Some\n",
    "  assert_eq!(n.or(n), n);    // None1 or None2 = None2\n",
    "\n",
    "  assert_eq!(o1.or(o2), o1); // Ok1 or Ok2 = Ok1\n",
    "  assert_eq!(o1.or(e1), o1); // Ok or Err = Ok\n",
    "  assert_eq!(e1.or(o1), o1); // Err or Ok = Ok\n",
    "  assert_eq!(e1.or(e2), e2); // Err1 or Err2 = Err2\n",
    "\n",
    "  assert_eq!(s1.and(s2), s2); // Some1 and Some2 = Some2\n",
    "  assert_eq!(s1.and(n), n);   // Some and None = None\n",
    "  assert_eq!(n.and(s1), n);   // None and Some = None\n",
    "  assert_eq!(n.and(n), n);    // None1 and None2 = None1\n",
    "\n",
    "  assert_eq!(o1.and(o2), o2); // Ok1 and Ok2 = Ok2\n",
    "  assert_eq!(o1.and(e1), e1); // Ok and Err = Err\n",
    "  assert_eq!(e1.and(o1), e1); // Err and Ok = Err\n",
    "  assert_eq!(e1.and(e2), e1); // Err1 and Err2 = Err1\n",
    "}\n",
    "\n",
    "main();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "除了 `or` 和 `and` 之外，Rust 还为我们提供了 `xor` ，但是它只能应用在 `Option` 上，其实想想也是这个理，如果能应用在 `Result` 上，那你又该如何对一个值和错误进行异或操作？"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [or_else() 和 and_then()](https://course.rs/advance/errors.html#or_else-和-and_then)\n",
    "\n",
    "它们跟 `or()` 和 `and()` 类似，唯一的区别在于，它们的第二个表达式是一个闭包。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "fn main() {\n",
    "    // or_else with Option\n",
    "    let s1 = Some(\"some1\");\n",
    "    let s2 = Some(\"some2\");\n",
    "    let fn_some = || Some(\"some2\"); // 类似于: let fn_some = || -> Option<&str> { Some(\"some2\") };\n",
    "\n",
    "    let n: Option<&str> = None;\n",
    "    let fn_none = || None;\n",
    "\n",
    "    assert_eq!(s1.or_else(fn_some), s1);  // Some1 or_else Some2 = Some1\n",
    "    assert_eq!(s1.or_else(fn_none), s1);  // Some or_else None = Some\n",
    "    assert_eq!(n.or_else(fn_some), s2);   // None or_else Some = Some\n",
    "    assert_eq!(n.or_else(fn_none), None); // None1 or_else None2 = None2\n",
    "\n",
    "    // or_else with Result\n",
    "    let o1: Result<&str, &str> = Ok(\"ok1\");\n",
    "    let o2: Result<&str, &str> = Ok(\"ok2\");\n",
    "    let fn_ok = |_| Ok(\"ok2\"); // 类似于: let fn_ok = |_| -> Result<&str, &str> { Ok(\"ok2\") };\n",
    "\n",
    "    let e1: Result<&str, &str> = Err(\"error1\");\n",
    "    let e2: Result<&str, &str> = Err(\"error2\");\n",
    "    let fn_err = |_| Err(\"error2\");\n",
    "\n",
    "    assert_eq!(o1.or_else(fn_ok), o1);  // Ok1 or_else Ok2 = Ok1\n",
    "    assert_eq!(o1.or_else(fn_err), o1); // Ok or_else Err = Ok\n",
    "    assert_eq!(e1.or_else(fn_ok), o2);  // Err or_else Ok = Ok\n",
    "    assert_eq!(e1.or_else(fn_err), e2); // Err1 or_else Err2 = Err2\n",
    "}\n",
    "\n",
    "main();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fn main() {\n",
    "    // and_then with Option\n",
    "    let s1 = Some(\"some1\");\n",
    "    let s2 = Some(\"some2\");\n",
    "    let fn_some = |_| Some(\"some2\"); // 类似于: let fn_some = |_| -> Option<&str> { Some(\"some2\") };\n",
    "\n",
    "    let n: Option<&str> = None;\n",
    "    let fn_none = |_| None;\n",
    "\n",
    "    assert_eq!(s1.and_then(fn_some), s2); // Some1 and_then Some2 = Some2\n",
    "    assert_eq!(s1.and_then(fn_none), n);  // Some and_then None = None\n",
    "    assert_eq!(n.and_then(fn_some), n);   // None and_then Some = None\n",
    "    assert_eq!(n.and_then(fn_none), n);   // None1 and_then None2 = None1\n",
    "\n",
    "    // and_then with Result\n",
    "    let o1: Result<&str, &str> = Ok(\"ok1\");\n",
    "    let o2: Result<&str, &str> = Ok(\"ok2\");\n",
    "    let fn_ok = |_| Ok(\"ok2\"); // 类似于: let fn_ok = |_| -> Result<&str, &str> { Ok(\"ok2\") };\n",
    "\n",
    "    let e1: Result<&str, &str> = Err(\"error1\");\n",
    "    let e2: Result<&str, &str> = Err(\"error2\");\n",
    "    let fn_err = |_| Err(\"error2\");\n",
    "\n",
    "    assert_eq!(o1.and_then(fn_ok), o2);  // Ok1 and_then Ok2 = Ok2\n",
    "    assert_eq!(o1.and_then(fn_err), e2); // Ok and_then Err = Err\n",
    "    assert_eq!(e1.and_then(fn_ok), e1);  // Err and_then Ok = Err\n",
    "    assert_eq!(e1.and_then(fn_err), e1); // Err1 and_then Err2 = Err1\n",
    "}\n",
    "\n",
    "main();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [filter](https://course.rs/advance/errors.html#filter)\n",
    "\n",
    "`filter` 用于对 `Option` 进行过滤："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "fn main() {\n",
    "    let s1 = Some(3);\n",
    "    let s2 = Some(6);\n",
    "    let n = None;\n",
    "\n",
    "    let fn_is_even = |x: &i8| x % 2 == 0;\n",
    "\n",
    "    assert_eq!(s1.filter(fn_is_even), n);  // Some(3) -> 3 is not even -> None\n",
    "    assert_eq!(s2.filter(fn_is_even), s2); // Some(6) -> 6 is even -> Some(6)\n",
    "    assert_eq!(n.filter(fn_is_even), n);   // None -> no value -> None\n",
    "}\n",
    "\n",
    "main();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [map() 和 map_err()](https://course.rs/advance/errors.html#map-和-map_err)\n",
    "\n",
    "`map` 可以将 `Some` 或 `Ok` 中的值映射为另一个："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "fn main() {\n",
    "    let s1 = Some(\"abcde\");\n",
    "    let s2 = Some(5);\n",
    "\n",
    "    let n1: Option<&str> = None;\n",
    "    let n2: Option<usize> = None;\n",
    "\n",
    "    let o1: Result<&str, &str> = Ok(\"abcde\");\n",
    "    let o2: Result<usize, &str> = Ok(5);\n",
    "\n",
    "    let e1: Result<&str, &str> = Err(\"abcde\");\n",
    "    let e2: Result<usize, &str> = Err(\"abcde\");\n",
    "\n",
    "    let fn_character_count = |s: &str| s.chars().count();\n",
    "\n",
    "    assert_eq!(s1.map(fn_character_count), s2); // Some1 map = Some2\n",
    "    assert_eq!(n1.map(fn_character_count), n2); // None1 map = None2\n",
    "\n",
    "    assert_eq!(o1.map(fn_character_count), o2); // Ok1 map = Ok2\n",
    "    assert_eq!(e1.map(fn_character_count), e2); // Err1 map = Err2\n",
    "}\n",
    "\n",
    "main();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "但是如果你想要将 `Err` 中的值进行改变， `map` 就无能为力了，此时我们需要用 `map_err`："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fn main() {\n",
    "    let o1: Result<&str, &str> = Ok(\"abcde\");\n",
    "    let o2: Result<&str, isize> = Ok(\"abcde\");\n",
    "\n",
    "    let e1: Result<&str, &str> = Err(\"404\");\n",
    "    let e2: Result<&str, isize> = Err(404);\n",
    "\n",
    "    let fn_character_count = |s: &str| -> isize { s.parse().unwrap() }; // 该函数返回一个 isize\n",
    "\n",
    "    assert_eq!(o1.map_err(fn_character_count), o2); // Ok1 map = Ok2\n",
    "    assert_eq!(e1.map_err(fn_character_count), e2); // Err1 map = Err2\n",
    "}\n",
    "\n",
    "main();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "通过对 `o1` 的操作可以看出，与 `map` 面对 `Err` 时的短小类似， `map_err` 面对 `Ok` 时也是相当无力的。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [map_or() 和 map_or_else()](https://course.rs/advance/errors.html#map_or-和-map_or_else)\n",
    "\n",
    "`map_or` 在 `map` 的基础上提供了一个默认值:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fn main() {\n",
    "    const V_DEFAULT: u32 = 1;\n",
    "\n",
    "    let s: Result<u32, ()> = Ok(10);\n",
    "    let n: Option<u32> = None;\n",
    "    let fn_closure = |v: u32| v + 2;\n",
    "\n",
    "    assert_eq!(s.map_or(V_DEFAULT, fn_closure), 12);\n",
    "    assert_eq!(n.map_or(V_DEFAULT, fn_closure), V_DEFAULT);\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如上所示，当处理 `None` 的时候，`V_DEFAULT` 作为默认值被直接返回。\n",
    "\n",
    "`map_or_else` 与 `map_or` 类似，但是它是通过一个闭包来提供默认值:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fn main() {\n",
    "    let s = Some(10);\n",
    "    let n: Option<i8> = None;\n",
    "\n",
    "    let fn_closure = |v: i8| v + 2;\n",
    "    let fn_default = || 1;\n",
    "\n",
    "    assert_eq!(s.map_or_else(fn_default, fn_closure), 12);\n",
    "    assert_eq!(n.map_or_else(fn_default, fn_closure), 1);\n",
    "\n",
    "    let o = Ok(10);\n",
    "    let e = Err(5);\n",
    "    let fn_default_for_result = |v: i8| v + 1; // 闭包可以对 Err 中的值进行处理，并返回一个新值\n",
    "\n",
    "    assert_eq!(o.map_or_else(fn_default_for_result, fn_closure), 12);\n",
    "    assert_eq!(e.map_or_else(fn_default_for_result, fn_closure), 6);\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [ok_or() and ok_or_else()](https://course.rs/advance/errors.html#ok_or-and-ok_or_else)\n",
    "\n",
    "这两兄弟可以将 `Option` 类型转换为 `Result` 类型。其中 `ok_or` 接收一个默认的 `Err` 参数:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fn main() {\n",
    "    const ERR_DEFAULT: &str = \"error message\";\n",
    "\n",
    "    let s = Some(\"abcde\");\n",
    "    let n: Option<&str> = None;\n",
    "\n",
    "    let o: Result<&str, &str> = Ok(\"abcde\");\n",
    "    let e: Result<&str, &str> = Err(ERR_DEFAULT);\n",
    "\n",
    "    assert_eq!(s.ok_or(ERR_DEFAULT), o); // Some(T) -> Ok(T)\n",
    "    assert_eq!(n.ok_or(ERR_DEFAULT), e); // None -> Err(default)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "而 `ok_or_else` 接收一个闭包作为 `Err` 参数:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fn main() {\n",
    "    let s = Some(\"abcde\");\n",
    "    let n: Option<&str> = None;\n",
    "    let fn_err_message = || \"error message\";\n",
    "\n",
    "    let o: Result<&str, &str> = Ok(\"abcde\");\n",
    "    let e: Result<&str, &str> = Err(\"error message\");\n",
    "\n",
    "    assert_eq!(s.ok_or_else(fn_err_message), o); // Some(T) -> Ok(T)\n",
    "    assert_eq!(n.ok_or_else(fn_err_message), e); // None -> Err(default)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "以上列出的只是常用的一部分，强烈建议大家看看标准库中有哪些可用的 API，在实际项目中，这些 API 将会非常有用: [Option](https://doc.rust-lang.org/stable/std/option/enum.Option.html) 和 [Result](https://doc.rust-lang.org/stable/std/result/enum.Result.html)。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# [自定义错误类型](https://course.rs/advance/errors.html#自定义错误类型)\n",
    "\n",
    "虽然标准库定义了大量的错误类型，但是一个严谨的项目，光使用这些错误类型往往是不够的，例如我们可能会为暴露给用户的错误定义相应的类型。\n",
    "\n",
    "为了帮助我们更好的定义错误，Rust 在标准库中提供了一些可复用的特征，例如 `std::error::Error` 特征："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "use std::fmt::{Debug, Display};\n",
    "\n",
    "pub trait Error: Debug + Display {\n",
    "    fn source(&self) -> Option<&(Error + 'static)> { ... }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "当自定义类型实现该特征后，该类型就可以作为 `Err` 来使用，下面一起来看看。\n",
    "\n",
    "> 实际上，自定义错误类型只需要实现 `Debug` 和 `Display` 特征即可，`source` 方法是可选的，而 `Debug` 特征往往也无需手动实现，可以直接通过 `derive` 来派生"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [最简单的错误](https://course.rs/advance/errors.html#最简单的错误)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "An Error Occurred, Please Try Again!\n",
      "Err(AppError)\n"
     ]
    }
   ],
   "source": [
    "use std::fmt;\n",
    "\n",
    "// AppError 是自定义错误类型，它可以是当前包中定义的任何类型，在这里为了简化，我们使用了单元结构体作为例子。\n",
    "// 为 AppError 自动派生 Debug 特征\n",
    "#[derive(Debug)]\n",
    "struct AppError;\n",
    "\n",
    "// 为 AppError 实现 std::fmt::Display 特征\n",
    "impl fmt::Display for AppError {\n",
    "    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n",
    "        write!(f, \"An Error Occurred, Please Try Again!\") // user-facing output\n",
    "    }\n",
    "}\n",
    "\n",
    "// 一个示例函数用于产生 AppError 错误\n",
    "fn produce_error() -> Result<(), AppError> {\n",
    "    Err(AppError)\n",
    "}\n",
    "\n",
    "fn main(){\n",
    "    match produce_error() {\n",
    "        Err(e) => eprintln!(\"{}\", e),\n",
    "        _ => println!(\"No error\"),\n",
    "    }\n",
    "\n",
    "    eprintln!(\"{:?}\", produce_error()); // Err({ file: src/main.rs, line: 17 })\n",
    "}\n",
    "\n",
    "main();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上面的例子很简单，我们定义了一个错误类型，当为它派生了 `Debug` 特征，同时手动实现了 `Display` 特征后，该错误类型就可以作为 `Err`来使用了。\n",
    "\n",
    "事实上，实现 `Debug` 和 `Display` 特征并不是作为 `Err` 使用的必要条件，大家可以把这两个特征实现和相应使用去除，然后看看代码会否报错。既然如此，我们为何要为自定义类型实现这两个特征呢？原因有二:\n",
    "\n",
    "- 错误得打印输出后，才能有实际用处，而打印输出就需要实现这两个特征\n",
    "- 可以将自定义错误转换成 `Box<dyn std::error:Error>` 特征对象，在后面的**归一化不同错误类型**部分，我们会详细介绍"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [更详尽的错误](https://course.rs/advance/errors.html#更详尽的错误)\n",
    "\n",
    "上一个例子中定义的错误非常简单，我们无法从错误中得到更多的信息，现在再来定义一个具有错误码和信息的错误:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Sorry, Can not find the Page!\n",
      "Err(AppError { code: 404, message: Page not found })\n",
      "Err(\n",
      "    AppError { code: 404, message: Page not found },\n",
      ")\n"
     ]
    }
   ],
   "source": [
    "use std::fmt;\n",
    "\n",
    "struct AppError {\n",
    "    code: usize,\n",
    "    message: String,\n",
    "}\n",
    "\n",
    "// 根据错误码显示不同的错误信息\n",
    "impl fmt::Display for AppError {\n",
    "    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n",
    "        let err_msg = match self.code {\n",
    "            404 => \"Sorry, Can not find the Page!\",\n",
    "            _ => \"Sorry, something is wrong! Please Try Again!\",\n",
    "        };\n",
    "\n",
    "        write!(f, \"{}\", err_msg)\n",
    "    }\n",
    "}\n",
    "\n",
    "impl fmt::Debug for AppError {\n",
    "    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n",
    "        write!(\n",
    "            f,\n",
    "            \"AppError {{ code: {}, message: {} }}\",\n",
    "            self.code, self.message\n",
    "        )\n",
    "    }\n",
    "}\n",
    "\n",
    "fn produce_error() -> Result<(), AppError> {\n",
    "    Err(AppError {\n",
    "        code: 404,\n",
    "        message: String::from(\"Page not found\"),\n",
    "    })\n",
    "}\n",
    "\n",
    "fn main() {\n",
    "    match produce_error() {\n",
    "        Err(e) => eprintln!(\"{}\", e), // 抱歉，未找到指定的页面!\n",
    "        _ => println!(\"No error\"),\n",
    "    }\n",
    "\n",
    "    eprintln!(\"{:?}\", produce_error()); // Err(AppError { code: 404, message: Page not found })\n",
    "\n",
    "    eprintln!(\"{:#?}\", produce_error());\n",
    "    // Err(\n",
    "    //     AppError { code: 404, message: Page not found }\n",
    "    // )\n",
    "}\n",
    "\n",
    "main();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在本例中，我们除了增加了错误码和消息外，还手动实现了 `Debug` 特征，原因在于，我们希望能自定义 `Debug` 的输出内容，而不是使用派生后系统提供的默认输出形式。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## * [错误转换 `From` 特征](https://course.rs/advance/errors.html#错误转换-from-特征)\n",
    "\n",
    "标准库、三方库、本地库，各有各的精彩，各也有各的错误。那么问题就来了，我们该如何将其它的错误类型转换成自定义的错误类型？总不能神鬼牛魔，同台共舞吧。。\n",
    "\n",
    "好在 Rust 为我们提供了 `std::convert::From` 特征:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pub trait From<T>: Sized {\n",
    "  fn from(_: T) -> Self;\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> 事实上，该特征在之前的 [`?` 操作符](https://course.rs/basic/result-error/result.html#传播界的大明星-)章节中就有所介绍。\n",
    ">\n",
    "> 大家都使用过 `String::from` 函数吧？它可以通过 `&str` 来创建一个 `String`，其实该函数就是 `From` 特征提供的\n",
    "\n",
    "下面一起来看看如何为自定义类型实现 `From` 特征:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "use std::fs::File;\n",
    "use std::io;\n",
    "\n",
    "#[derive(Debug)]\n",
    "struct AppError {\n",
    "    kind: String,    // 错误类型\n",
    "    message: String, // 错误信息\n",
    "}\n",
    "\n",
    "// 为 AppError 实现 std::convert::From 特征，由于 From 包含在 std::prelude 中，因此可以直接简化引入。\n",
    "// 实现 From<io::Error> 意味着我们可以将 io::Error 错误转换成自定义的 AppError 错误\n",
    "impl From<io::Error> for AppError {\n",
    "    fn from(error: io::Error) -> Self {\n",
    "        AppError {\n",
    "            kind: String::from(\"io\"),\n",
    "            message: error.to_string(),\n",
    "        }\n",
    "    }\n",
    "}\n",
    "\n",
    "fn main() -> Result<(), AppError> {\n",
    "    let _file = File::open(\"nonexistent_file.txt\")?;\n",
    "\n",
    "    Ok(())\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上面的代码中除了实现 `From` 外，还有一点特别重要，那就是 `?` 可以将错误进行隐式的强制转换：`File::open` 返回的是 `std::io::Error`， 我们并没有进行任何显式的转换，它就能自动变成 `AppError` ，这就是 `?` 的强大之处！\n",
    "\n",
    "上面的例子只有一个标准库错误，再来看看多个不同的错误转换成 `AppError` 的实现："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "use std::fs::File;\n",
    "use std::io::{self, Read};\n",
    "use std::num;\n",
    "\n",
    "#[derive(Debug)]\n",
    "struct AppError {\n",
    "    kind: String,\n",
    "    message: String,\n",
    "}\n",
    "\n",
    "impl From<io::Error> for AppError {\n",
    "    fn from(error: io::Error) -> Self {\n",
    "        AppError {\n",
    "            kind: String::from(\"io\"),\n",
    "            message: error.to_string(),\n",
    "        }\n",
    "    }\n",
    "}\n",
    "\n",
    "impl From<num::ParseIntError> for AppError {\n",
    "    fn from(error: num::ParseIntError) -> Self {\n",
    "        AppError {\n",
    "            kind: String::from(\"parse\"),\n",
    "            message: error.to_string(),\n",
    "        }\n",
    "    }\n",
    "}\n",
    "\n",
    "fn main() -> Result<(), AppError> {\n",
    "    let mut file = File::open(\"hello_world.txt\")?;\n",
    "\n",
    "    let mut content = String::new();\n",
    "    file.read_to_string(&mut content)?;\n",
    "\n",
    "    let _number: usize;\n",
    "    _number = content.parse()?;\n",
    "\n",
    "    Ok(())\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# [归一化不同的错误类型](https://course.rs/advance/errors.html#归一化不同的错误类型)\n",
    "\n",
    "至此，关于 Rust 的错误处理大家已经了若指掌了，下面再来看看一些实战中的问题。\n",
    "\n",
    "在实际项目中，我们往往会为不同的错误定义不同的类型，这样做非常好，但是如果你要在一个函数中返回不同的错误呢？例如："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "ename": "Error",
     "evalue": "`?` couldn't convert the error to `std::io::Error`",
     "output_type": "error",
     "traceback": [
      "\u001b[31m[E0277] Error:\u001b[0m `?` couldn't convert the error to `std::io::Error`",
      "    \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_9:1:1 \u001b[38;5;246m]\u001b[0m",
      "    \u001b[38;5;246m│\u001b[0m",
      " \u001b[38;5;246m 9 │\u001b[0m \u001b[38;5;249mf\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m-\u001b[0m\u001b[38;5;249m>\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;100mR\u001b[0m\u001b[38;5;100me\u001b[0m\u001b[38;5;100ms\u001b[0m\u001b[38;5;100mu\u001b[0m\u001b[38;5;100ml\u001b[0m\u001b[38;5;100mt\u001b[0m\u001b[38;5;100m<\u001b[0m\u001b[38;5;100mS\u001b[0m\u001b[38;5;100mt\u001b[0m\u001b[38;5;100mr\u001b[0m\u001b[38;5;100mi\u001b[0m\u001b[38;5;100mn\u001b[0m\u001b[38;5;100mg\u001b[0m\u001b[38;5;100m,\u001b[0m\u001b[38;5;100m \u001b[0m\u001b[38;5;100ms\u001b[0m\u001b[38;5;100mt\u001b[0m\u001b[38;5;100md\u001b[0m\u001b[38;5;100m:\u001b[0m\u001b[38;5;100m:\u001b[0m\u001b[38;5;100mi\u001b[0m\u001b[38;5;100mo\u001b[0m\u001b[38;5;100m:\u001b[0m\u001b[38;5;100m:\u001b[0m\u001b[38;5;100mE\u001b[0m\u001b[38;5;100mr\u001b[0m\u001b[38;5;100mr\u001b[0m\u001b[38;5;100mo\u001b[0m\u001b[38;5;100mr\u001b[0m\u001b[38;5;100m>\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m{\u001b[0m",
      " \u001b[38;5;240m   │\u001b[0m                \u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m┬\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m  ",
      " \u001b[38;5;240m   │\u001b[0m                               \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m expected `std::io::Error` because of this",
      " \u001b[38;5;246m10 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mf\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54ms\u001b[0m\u001b[38;5;54mt\u001b[0m\u001b[38;5;54md\u001b[0m\u001b[38;5;54m:\u001b[0m\u001b[38;5;54m:\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;54mn\u001b[0m\u001b[38;5;54mv\u001b[0m\u001b[38;5;54m:\u001b[0m\u001b[38;5;54m:\u001b[0m\u001b[38;5;54mv\u001b[0m\u001b[38;5;54ma\u001b[0m\u001b[38;5;54mr\u001b[0m\u001b[38;5;54m(\u001b[0m\u001b[38;5;54m\"\u001b[0m\u001b[38;5;54mM\u001b[0m\u001b[38;5;54mA\u001b[0m\u001b[38;5;54mR\u001b[0m\u001b[38;5;54mK\u001b[0m\u001b[38;5;54mD\u001b[0m\u001b[38;5;54mO\u001b[0m\u001b[38;5;54mW\u001b[0m\u001b[38;5;54mN\u001b[0m\u001b[38;5;54m\"\u001b[0m\u001b[38;5;54m)\u001b[0m\u001b[38;5;68m?\u001b[0m\u001b[38;5;249m;\u001b[0m",
      " \u001b[38;5;240m   │\u001b[0m              \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;68m┬\u001b[0m  ",
      " \u001b[38;5;240m   │\u001b[0m                          \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m this can't be annotated with `?` because it has type `Result<_, VarError>`",
      " \u001b[38;5;240m   │\u001b[0m                                       \u001b[38;5;68m│\u001b[0m  ",
      " \u001b[38;5;240m   │\u001b[0m                                       \u001b[38;5;68m╰\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m the trait `From<VarError>` is not implemented for `std::io::Error`",
      "\u001b[38;5;246m────╯\u001b[0m"
     ]
    }
   ],
   "source": [
    "use std::fs::read_to_string;\n",
    "\n",
    "fn main() -> Result<(), std::io::Error> {\n",
    "  let html = render()?;\n",
    "  println!(\"{}\", html);\n",
    "  Ok(())\n",
    "}\n",
    "\n",
    "fn render() -> Result<String, std::io::Error> {\n",
    "  let file = std::env::var(\"MARKDOWN\")?;\n",
    "  let source = read_to_string(file)?;\n",
    "  Ok(source)\n",
    "}\n",
    "\n",
    "main();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上面的代码会报错，原因在于 `render` 函数中的两个 `?` 返回的实际上是不同的错误：`env::var()` 返回的是 `std::env::VarError`，而 `read_to_string` 返回的是 `std::io::Error`。\n",
    "\n",
    "为了满足 `render` 函数的签名，我们就需要将 `env::VarError` 和 `io::Error` 归一化为同一种错误类型。要实现这个目的有三种方式:\n",
    "\n",
    "- 使用特征对象 `Box<dyn Error>`\n",
    "- 自定义错误类型\n",
    "- 使用 `thiserror`\n",
    "\n",
    "下面依次来看看相关的解决方式。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [`Box`](https://course.rs/advance/errors.html#boxdyn-error)\n",
    "\n",
    "大家还记得我们之前提到的 `std::error::Error` 特征吧，当时有说：自定义类型实现 `Debug + Display` 特征的主要原因就是为了能转换成 `Error` 的特征对象，而特征对象恰恰是在同一个地方使用不同类型的关键:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "use std::fs::read_to_string;\n",
    "use std::error::Error;\n",
    "fn main() -> Result<(), Box<dyn Error>> {\n",
    "  let html = render()?;\n",
    "  println!(\"{}\", html);\n",
    "  Ok(())\n",
    "}\n",
    "\n",
    "fn render() -> Result<String, Box<dyn Error>> {\n",
    "  let file = std::env::var(\"MARKDOWN\")?;\n",
    "  let source = read_to_string(file)?;\n",
    "  Ok(source)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这个方法很简单，在绝大多数场景中，性能也非常够用，但是有一个问题：`Result` 实际上不会限制错误的类型，也就是一个类型就算不实现 `Error` 特征，它依然可以在 `Result<T, E>` 中作为 `E` 来使用，此时这种特征对象的解决方案就无能为力了。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [自定义错误类型](https://course.rs/advance/errors.html#自定义错误类型-1)\n",
    "\n",
    "与特征对象相比，自定义错误类型麻烦归麻烦，但是它非常灵活，因此也不具有上面的类似限制:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "use std::fs::read_to_string;\n",
    "\n",
    "fn main() -> Result<(), MyError> {\n",
    "  let html = render()?;\n",
    "  println!(\"{}\", html);\n",
    "  Ok(())\n",
    "}\n",
    "\n",
    "fn render() -> Result<String, MyError> {\n",
    "  let file = std::env::var(\"MARKDOWN\")?;\n",
    "  let source = read_to_string(file)?;\n",
    "  Ok(source)\n",
    "}\n",
    "\n",
    "#[derive(Debug)]\n",
    "enum MyError {\n",
    "  EnvironmentVariableNotFound,\n",
    "  IOError(std::io::Error),\n",
    "}\n",
    "\n",
    "impl From<std::env::VarError> for MyError {\n",
    "  fn from(_: std::env::VarError) -> Self {\n",
    "    Self::EnvironmentVariableNotFound\n",
    "  }\n",
    "}\n",
    "\n",
    "impl From<std::io::Error> for MyError {\n",
    "  fn from(value: std::io::Error) -> Self {\n",
    "    Self::IOError(value)\n",
    "  }\n",
    "}\n",
    "\n",
    "impl std::error::Error for MyError {}\n",
    "\n",
    "impl std::fmt::Display for MyError {\n",
    "  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n",
    "    match self {\n",
    "      MyError::EnvironmentVariableNotFound => write!(f, \"Environment variable not found\"),\n",
    "      MyError::IOError(err) => write!(f, \"IO Error: {}\", err.to_string()),\n",
    "    }\n",
    "  }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上面代码中有一行值得注意：`impl std::error::Error for MyError {}` ，只有为自定义错误类型实现 `Error` 特征后，才能转换成相应的特征对象。\n",
    "\n",
    "不得不说，真是啰嗦啊。因此在能用特征对象的时候，建议大家还是使用特征对象，无论如何，代码可读性还是很重要的！\n",
    "\n",
    "上面的第二种方式灵活归灵活，啰嗦也是真啰嗦，好在 Rust 的社区为我们提供了 `thiserror` 解决方案，下面一起来看看该如何简化 Rust 中的错误处理。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# [简化错误处理](https://course.rs/advance/errors.html#简化错误处理)\n",
    "\n",
    "对于开发者而言，错误处理是代码中打交道最多的部分之一，因此选择一把趁手的武器也很重要，它可以帮助我们节省大量的时间和精力，好钢应该用在代码逻辑而不是冗长的错误处理上。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [thiserror](https://course.rs/advance/errors.html#thiserror)\n",
    "\n",
    "[`thiserror`](https://github.com/dtolnay/thiserror)可以帮助我们简化上面的第二种解决方案："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "use std::fs::read_to_string;\n",
    "\n",
    "fn main() -> Result<(), MyError> {\n",
    "  let html = render()?;\n",
    "  println!(\"{}\", html);\n",
    "  Ok(())\n",
    "}\n",
    "\n",
    "fn render() -> Result<String, MyError> {\n",
    "  let file = std::env::var(\"MARKDOWN\")?;\n",
    "  let source = read_to_string(file)?;\n",
    "  Ok(source)\n",
    "}\n",
    "\n",
    "#[derive(thiserror::Error, Debug)]\n",
    "enum MyError {\n",
    "  #[error(\"Environment variable not found\")]\n",
    "  EnvironmentVariableNotFound(#[from] std::env::VarError),\n",
    "  #[error(transparent)]\n",
    "  IOError(#[from] std::io::Error),\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如上所示，只要简单写写注释，就可以实现错误处理了，惊不惊喜？"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [error-chain](https://course.rs/advance/errors.html#error-chain)\n",
    "\n",
    "[`error-chain`](https://github.com/rust-lang-deprecated/error-chain) 也是简单好用的库，可惜不再维护了，但是我觉得它依然可以在合适的地方大放光彩，值得大家去了解下。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "use std::fs::read_to_string;\n",
    "\n",
    "error_chain::error_chain! {\n",
    "  foreign_links {\n",
    "    EnvironmentVariableNotFound(::std::env::VarError);\n",
    "    IOError(::std::io::Error);\n",
    "  }\n",
    "}\n",
    "\n",
    "fn main() -> Result<()> {\n",
    "  let html = render()?;\n",
    "  println!(\"{}\", html);\n",
    "  Ok(())\n",
    "}\n",
    "\n",
    "fn render() -> Result<String> {\n",
    "  let file = std::env::var(\"MARKDOWN\")?;\n",
    "  let source = read_to_string(file)?;\n",
    "  Ok(source)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "source": [
    "喏，简单吧？使用 `error-chain` 的宏你可以获得：`Error` 结构体，错误类型 `ErrorKind` 枚举 以及一个自定义的 `Result` 类型。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [anyhow](https://course.rs/advance/errors.html#anyhow)\n",
    "\n",
    "[`anyhow`](https://github.com/dtolnay/anyhow) 和 `thiserror` 是同一个作者开发的，这里是作者关于 `anyhow` 和 `thiserror` 的原话：\n",
    "\n",
    "> 如果你想要设计自己的错误类型，同时给调用者提供具体的信息时，就使用 `thiserror`，例如当你在开发一个三方库代码时。如果你只想要简单，就使用 `anyhow`，例如在自己的应用服务中。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "use std::fs::read_to_string;\n",
    "\n",
    "use anyhow::Result;\n",
    "\n",
    "fn main() -> Result<()> {\n",
    "    let html = render()?;\n",
    "    println!(\"{}\", html);\n",
    "    Ok(())\n",
    "}\n",
    "\n",
    "fn render() -> Result<String> {\n",
    "    let file = std::env::var(\"MARKDOWN\")?;\n",
    "    let source = read_to_string(file)?;\n",
    "    Ok(source)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# [总结](https://course.rs/advance/errors.html#总结)\n",
    "\n",
    "Rust 一个为人津津乐道的点就是强大、易用的错误处理，对于新手来说，这个机制可能"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Rust",
   "language": "rust",
   "name": "rust"
  },
  "language_info": {
   "codemirror_mode": "rust",
   "file_extension": ".rs",
   "mimetype": "text/rust",
   "name": "rust",
   "pygment_lexer": "rust",
   "version": ""
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
